package com.github.yoojia.flow;

import com.parkingwang.lang.kit.ByteKit;

import java.util.Objects;
import java.util.StringJoiner;

import static com.parkingwang.lang.kit.ByteKit.fromFloat;
import static com.parkingwang.lang.kit.ByteKit.fromInt;

/**
 * @author 陈小锅 (yoojiachen@gmail.com)
 * @since 1.0.0
 */
public interface Payload {


    class Bytes {

        private final byte[] mBytes;

        public Bytes(byte[] bytes) {
            this.mBytes = bytes;
        }

        public byte[] getBytes() {
            return mBytes;
        }

        public String getHexString() {
            return ByteKit.toHex(mBytes);
        }

        public static Bytes create(byte[] bytes) {
            return new Bytes(bytes);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Bytes that = (Bytes) o;
            return Objects.equals(this.mBytes, that.mBytes);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mBytes);
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
                    .add("bytes = " + mBytes)
                    .toString();
        }

        static byte[] getBytesOf(byte[] bytes, int index) {
            if (bytes.length < 4) {
                return new byte[0];
            } else {
                final int startPos = index * 4;
                if (startPos < bytes.length) {
                    final byte[] output = new byte[4];
                    System.arraycopy(bytes, startPos, output, 0, 4);
                    return output;
                } else {
                    return new byte[0];
                }
            }
        }

        static byte[] bytesOf(byte[] b1, byte[] b2, byte[] b3) {
            final byte[] bytes = new byte[4 * 3];
            System.arraycopy(b1, 0, bytes, 0, 4);
            System.arraycopy(b2, 0, bytes, 4, 4);
            System.arraycopy(b3, 0, bytes, 8, 4);
            return bytes;
        }
    }

    //////

    final class Bool extends Bytes {

        public final boolean bool;

        public Bool(boolean bool) {
            super(ByteKit.fromInt(bool ? 1 : 0));
            this.bool = bool;
        }

        public Bool(byte[] bytes) {
            super(bytes);
            this.bool = ByteKit.toInt(bytes) > 0;
        }

        public static Bool create(byte[] intBytes) {
            return new Bool(intBytes);
        }

        public static Bool create(boolean bool) {
            return new Bool(bool);
        }

        public static Bool create(int value) {
            return create(value >= 0);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Bool that = (Bool) o;
            return Objects.equals(this.bool, that.bool);
        }

        @Override
        public int hashCode() {
            return Objects.hash(bool);
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
                    .add("bool = " + bool)
                    .toString();
        }
    }

    //////


    final class Float extends Bytes {

        public final float float1;
        public final float float2;
        public final float float3;

        public Float(byte[] bytes) {
            super(bytes);
            this.float1 = floatOf(bytes, 0);
            this.float2 = floatOf(bytes, 1);
            this.float3 = floatOf(bytes, 2);
        }

        public Float(float float1, float float2, float float3) {
            super(bytesOf(fromFloat(float1), fromFloat(float2), fromFloat(float3)));
            this.float1 = float1;
            this.float2 = float2;
            this.float3 = float3;
        }

        public static Float create(byte[] bytes) {
            return new Float(bytes);
        }

        public static Float create(float value1, float value2, float value3) {
            return new Float(value1, value2, value3);
        }

        public static Float create(float value1, float value2) {
            return create(value1, value2, 0);
        }

        public static Float create(float value) {
            return create(value, 0, 0);
        }

        private static float floatOf(byte[] bytes, int index) {
            final byte[] b = getBytesOf(bytes, index);
            return b.length > 0 ? ByteKit.toFloat(b) : 0;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Float that = (Float) o;
            return Objects.equals(this.float1, that.float1) &&
                    Objects.equals(this.float2, that.float2) &&
                    Objects.equals(this.float3, that.float3);
        }

        @Override
        public int hashCode() {
            return Objects.hash(float1, float2, float3);
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
                    .add("float1 = " + float1)
                    .add("float2 = " + float2)
                    .add("float3 = " + float3)
                    .toString();
        }

    }

    //////


    final class Int extends Bytes {

        public final int int1;
        public final int int2;
        public final int int3;

        public Int(byte[] bytes) {
            super(bytes);
            this.int1 = intOf(bytes, 0);
            this.int2 = intOf(bytes, 1);
            this.int3 = intOf(bytes, 2);
        }

        public Int(int int1, int int2, int int3) {
            super(bytesOf(fromInt(int1), fromInt(int2), fromInt(int3)));
            this.int1 = int1;
            this.int2 = int2;
            this.int3 = int3;
        }

        public static Int create(byte[] bytes) {
            return new Int(bytes);
        }

        public static Int create(int value1, int value2, int value3) {
            return new Int(value1, value2, value3);
        }

        public static Int create(int value1, int value2) {
            return create(value1, value2, 0);
        }

        public static Int create(int value) {
            return create(value, 0, 0);
        }

        private static int intOf(byte[] bytes, int index) {
            final byte[] b = getBytesOf(bytes, index);
            return b.length > 0 ? ByteKit.toInt(b) : 0;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Int that = (Int) o;
            return Objects.equals(this.int1, that.int1) &&
                    Objects.equals(this.int2, that.int2) &&
                    Objects.equals(this.int3, that.int3);
        }

        @Override
        public int hashCode() {
            return Objects.hash(int1, int2, int3);
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
                    .add("int1 = " + int1)
                    .add("int2 = " + int2)
                    .add("int3 = " + int3)
                    .toString();
        }

    }

    //////


    final class Text extends Bytes {

        private transient String mTextRef;

        public Text(String text) {
            super(text.getBytes());
            this.mTextRef = text;
        }

        public Text(byte[] bytes) {
            super(bytes);
            this.mTextRef = null;
        }

        public String getText() {
            if (mTextRef == null) {
                mTextRef = new String(getBytes());
            }
            return mTextRef;
        }

        public static Text create(byte[] bytes) {
            return new Text(bytes);
        }

        public static Text create(String value) {
            return new Text(value);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Text that = (Text) o;
            return Objects.equals(this.getText(), that.getText());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getText());
        }

        @Override
        public String toString() {
            return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]")
                    .add("text = " + getText())
                    .toString();
        }
    }
}
